package com.hzlj.position.config.rest.operator.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fz.common.base.exception.PubException;
import com.hzlj.position.config.common.dto.common.PositionAddDTO;
import com.hzlj.position.config.common.dto.operatorActivation.OperatorActivationExtDTO;
import com.hzlj.position.config.common.dto.operatorConfig.OperatorConfigQueryResultDTO;
import com.hzlj.position.config.common.enums.WarningType;
import com.hzlj.position.config.rest.LonLatAddressRest;
import com.hzlj.position.config.rest.operator.OperatorLocateRest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;

/**
 * 移动
 * LE 协议
 *
 * @author lifh
 **/
@Slf4j
@Service("mobileLocateRestLEV1")
public class MobileLocateRestLEV1 implements OperatorLocateRest {
    @Resource
    private LonLatAddressRest lonLatAddressRest;

    @Override
    public PositionAddDTO fetchPosition(OperatorConfigQueryResultDTO config,
                                        String jgCode,
                                        String telephone,
                                        BiFunction<String, String, String> dictFun) {
        String requestResult = this.request(
                serviceId(jgCode, config),
                telephone,
                config.getLocateRequestUrl(),
                config.getLocateRequestConfig());

        PositionAddDTO position = defaultPosition(telephone, config);
        position.setWarningType(WarningType.FAIL.getCode());

        if (ObjectUtil.isEmpty(requestResult)) {
            log.warn("运营商-{}-请求结果：{}->{}", operatorName(), telephone, requestResult);
            return position;
        }
        try {
            log.debug("运营商-{}-请求结果：{}->{}", operatorName(), telephone, requestResult);
            JSONObject json = JSONUtil.xmlToJson(requestResult);

            JSONObject svcResult = json.getJSONObject("svc_result");
            JSONObject slia = svcResult.getJSONObject("slia");
            if (ObjectUtil.isEmpty(slia)) {
                position.setPosition(StrUtil.format("【{}】{}", "-1", "定位失败，结果解析失败"));
                return position;
            }
            JSONObject pos = slia.getJSONObject("pos");
            JSONObject result = slia.getJSONObject("result");
            JSONObject poserr = pos != null ? pos.getJSONObject("poserr") : null;
            if (poserr != null) {
                result = poserr.getJSONObject("result");
            }
            //无位置信息
            if (result != null) {
                String resid = result.getStr("resid");
                WarningType warningType = config.getExt().checkWarningType(resid);
                if (warningType != null) {
                    position.setWarningType(warningType.getCode());
                }
                //设置错误码
                position.setOperatorReturnCode(resid);
                position.setPosition(errorMsg(resid, dictFun));
                return position;
            }
            if (pos == null) {
                position.setPosition(StrUtil.format("【{}】{}", "-1", "定位失败，没有位置信息"));
                return position;
            }

            //有位置信息
            JSONObject pd = pos.getJSONObject("pd");
            JSONObject shape = pd.getJSONObject("shape");
            JSONObject point = shape.getJSONObject("Point");
            JSONObject coord = point != null ? point.getJSONObject("coord") : shape.getJSONObject("CircularArea").getJSONObject("coord");
            position.setLatitude(coord.getStr("X"));
            position.setLongitude(coord.getStr("Y"));
            position.setWarningType(WarningType.SUCCESS.getCode());
            position.setPosition(
                    lonLatAddressRest.getAddress(position.getLongitude(), position.getLatitude())
            );
        } catch (Exception e) {
            log.error("定位解析失败：telephone={},result={}", telephone, requestResult, e);
            position.setPosition(StrUtil.format("【{}】{}", "-1", "定位失败"));
        }
        return position;
    }

    @Override
    public void enablePosition(OperatorConfigQueryResultDTO config,
                               OperatorActivationExtDTO ext,
                               String jgCode,
                               String telephone) {
        ext.setServiceId(serviceId(jgCode, config));
        String requestResult = this.request(
                ext.getServiceId(),
                telephone,
                config.getLocateEnableRequestUrl(),
                config.getLocateEnableRequestConfig());

        JSONObject json = JSONUtil.xmlToJson(requestResult);
        log.info("运营商-{}-开通定位请求结果：{}->{}", operatorName(), telephone, json);
        try {
            JSONObject response = json.getJSONObject("sync_req").getJSONObject("body").getJSONObject("response");
            String resid = response.getStr("resid");
            if ("000".equals(resid)) {
                return;
            }
            String errors = response.getStr("errors");
            throw new PubException(String.format("开通定位返回失败:[%s=%s]", resid, errors));
        } catch (PubException e) {
            throw e;
        } catch (Exception e) {
            log.error("运营商-{}-开通定位请求解析失败：{}->{}", operatorName(), telephone, json);
            throw new PubException(String.format("开通定位请求解析失败:%s", telephone));
        }
    }

    @Override
    public void disablePosition(OperatorConfigQueryResultDTO config,
                                OperatorActivationExtDTO ext,
                                String jgCode,
                                String telephone) {
        String serviceId = serviceId(jgCode, config);
        if (ext != null && ext.getServiceId() != null) {
            serviceId = ext.getServiceId();
        }
        String requestResult = null;
        try {
            requestResult = this.request(
                    serviceId,
                    telephone,
                    config.getLocateDisableRequestUrl(),
                    config.getLocateDisableRequestConfig());
            log.info("运营商-{}-取消定位请求结果：{}->{}", operatorName(), telephone, requestResult);
            JSONObject json = JSONUtil.xmlToJson(requestResult);
            JSONObject response = json.getJSONObject("sync_req").getJSONObject("body").getJSONObject("response");
            String resid = response.getStr("resid");
            if ("000".equals(resid)) {
                return;
            }
            String errors = response.getStr("errors");
            throw new PubException(String.format("取消定位返回失败:[%s=%s]", resid, errors));
        } catch (PubException e) {
            throw e;
        } catch (Exception e) {
            log.error("运营商-{}-取消定位请求解析失败：{}->{}", operatorName(), telephone, requestResult);
            throw new PubException(String.format("取消定位请求解析失败:%s", telephone));
        }
    }

    private String errorMsg(String resid, BiFunction<String, String, String> dictFun) {
        String positionDesc = dictFun.apply(operatorDictType(), resid);
        return StrUtil.format("【{}】{}", resid, StrUtil.emptyToDefault(positionDesc, "未知错误"));
    }

    private String request(String serviceId,
                           String telephone,
                           String url,
                           String requestXml) {
        requestXml = requestXml
                .replaceAll("#telephone", telephone)
                .replaceAll("#serviceId", serviceId)
                .replaceAll("#sequence", sequence());

        HttpResponse execute = null;
        try {
            execute = HttpRequest.post(url)
                    .header("Content-type", "text/xml")
                    .header("charset", "ISO-8859-1")
                    .timeout(FETCH_LOCATE_TIMEOUT)
                    .body(requestXml)
                    .charset("GBK")
                    .execute();
            return execute.body();
        } catch (Exception e) {
            log.error("运营商-{}-定位请求失败：{}->{}(serviceId={})", operatorName(), telephone, serviceId, requestXml, e);
            throw e;
        } finally {
            IoUtil.close(execute);
        }
    }

    @Override
    public String operatorName() {
        return "移动";
    }

    @Override
    public String operatorDictType() {
        return "LOCATE_MOBILE_LE_V1";
    }


    private String serviceId(String jgCode, OperatorConfigQueryResultDTO config) {
        if (config.getExt() == null) {
            throw new PubException("没有配置serviceId");
        }
        Map<String, String> serviceIds = config.getExt().getServiceIds();
        if (ObjectUtil.isEmpty(serviceIds)) {
            throw new PubException("没有配置serviceId");
        }
        Optional<String> first = serviceIds.keySet().stream().filter(jgCode::startsWith).max(String::compareTo);
        return serviceIds.get(
                first.orElseThrow(() -> new PubException("没有匹配的serviceId"))
        );
    }

    private String sequence() {
        return System.currentTimeMillis() + "" + RandomUtil.randomInt(0, 9);
    }
}
